﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SequenceAssembler
{
	class TipRemover
	{
		public BackGraph bg { get; private set;}

		public TipRemover(BackGraph backGraph) {
			bg = backGraph;
		}

		public void Remove() {
			long before = bg.CellCount;
			long after = 0;
			SimpleCellMerger merger = new SimpleCellMerger(bg);
			while (before != after)
			{
				Console.WriteLine("Pre remove cell count: {0}", bg.CellCount);
				before = bg.CellCount;
				RemoveOnce();
				merger.Merge();
				after = bg.CellCount;
				Console.WriteLine("Post remove cell count: {0}", bg.CellCount);
			}
		}

		/// <summary>
		/// Remove tip that
		/// 1. has no incoming arc
		/// 2. only one outgoing arc
		/// 3. shorter than 2*KmerLen
		/// 4. has a stronger replacement
		/// </summary>
		private void RemoveOnce() {
			List<long> celllist = new List<long>(bg.CellList);
			foreach (long id in celllist)
			{
				SequenceCell cell = bg.LoadCell<SequenceCell>(id);
				if (cell == null) { continue; }

				if (cell.Size >= 2 * bg.KmerLen) { continue; } //3.

				if (cell.Arcs.Count != 1) { continue; } //2.

				long twinId = bg.TwinId(id);
				SequenceCell twinCell = bg.LoadCell<SequenceCell>(twinId);
				if (twinCell.Arcs.Count != 0) { continue; } //1.

				SequenceCell preTwinCell = bg.LoadCell<SequenceCell>(bg.TwinId(cell.Arcs[0].Destination));
				if (preTwinCell.Arcs.Count == 1) { continue; } // should be merged rather than dropped

				foreach (var arc in preTwinCell.Arcs)
				{
					if (arc.Destination == twinId) { continue; }
					if (arc.Multiplicity > cell.Arcs[0].Multiplicity) {
						RemoveTip(cell, twinCell, preTwinCell, twinId);
						break;
					}
				}
			}
		}

		private void RemoveTip(SequenceCell cell, SequenceCell twinCell, SequenceCell toCorrect, long toRemove)
		{
			bg.DeleteCell(cell);
			bg.DeleteCell(twinCell);
			for (int i = 0; i < toCorrect.Arcs.Count; ++i) {
				if (toCorrect.Arcs[i].Destination == toRemove) {
					toCorrect.Arcs.RemoveAt(i--);
				}
			}
			bg.SaveCell(toCorrect);
		}
	}
}
